import cantools
from colorama import Fore, init
import os
import sys
import warnings
import subprocess
import cantools
import pandas as pd

warnings.filterwarnings('error')  
print("Script name:", sys.argv[0])
if len(sys.argv)!=2:
    print("The number of parameters entered is incorrect.")
    sys.exit()
current_path=sys.argv[1]
flag=True
def start_bit_can2normal(start_bit):
    return (start_bit // 8) * 8 + (7 - start_bit % 8)
can_dict={}
can_dict_id={}
dbc_check=True
print(Fore.YELLOW+f"============dbc check============")

for root, dirs, files in os.walk(current_path):
    print(files)
    for file in files:
        path = os.path.join(root, file)     
        if ".dbc" in path:
            if 'CAN' in file:
                pos=file.find('CAN')
                can_name=file[pos:pos+5]
                #print(Fore.YELLOW+can_name)
                #print(can_dict.keys())
                if can_name in can_dict.keys():
                    pass
                else:
                    can_dict[can_name]={}
                    can_dict_id[can_name]={}
            else:
                can_name=file
                can_dict[can_name]={}
                can_dict_id[can_name]={}
            try:
                db = cantools.database.load_file(path)
                #print(db.messages)
                erro_sig={}
                mID={}
                erro_mID={}
                for message in db.messages:
                    if message.name in erro_sig.keys():
                        print(Fore.RED+f"Reason for error:multi message name in same dbc file-->[{file}](ID:{hex(int(message.frame_id))}({message.frame_id}),{message.name})")
                        flag=False
                    if message.frame_id in erro_mID.keys():
                        print(Fore.RED+f"Reason for error:multi message ID in same dbc file-->[{file}](ID:{hex(int(message.frame_id))}({message.frame_id}),{message.name})")
                        flag=False
                    #print(can_dict[can_name].keys())
                    if message.name in can_dict[can_name].keys():
                        print(Fore.RED+f"Reason for error:multi message name in different dbc file-->[{can_dict[can_name][message.name]},{file}](ID:{hex(int(message.frame_id))}({message.frame_id}),{message.name})")
                        flag=False
                    # if message.frame_id in can_dict[id]:
                    #     print(Fore.RED+f"ERROR Multi message ID in different dbc file(ID:{hex(int(message.frame_id))}({message.frame_id}),{message.name})")
                    #     flag=False
                    else:
                        can_dict[can_name][message.name]=file
                    if message.frame_id in can_dict_id[can_name].keys():
                        print(Fore.RED+f"Reason for error:multi message ID in different dbc file-->[{can_dict_id[can_name][message.frame_id]},{file}](ID:{hex(int(message.frame_id))}({message.frame_id}),{message.name})")
                        flag=False
                    else:
                        can_dict_id[can_name][message.frame_id]=file
                    erro_sig[message.name]=[]
                    mID[message.name]=message.frame_id
                    erro_mID[message.frame_id]=0
                    #print(Fore.GREEN+f"{message.frame_id},{message.name},length:{message.length*8}")
                    curr_message = db.get_message_by_name(message.name)
                    curr_start=0
                    last_start=0
                    for signal in curr_message.signals:
                        sigName_l=signal.name.lower()
                        #判断是摩托罗拉还是Intel
                        if signal.byte_order=='big_endian':
                            curr_start=start_bit_can2normal(signal.start)
                        elif signal.byte_order=='little_endian':
                            curr_start=signal.start
                        #该部分用于判断这次一个start位是否超过上一次的end位
                        if curr_start>=last_start:
                            #print(Fore.GREEN+f"ok!curr_start:{curr_start},last_start:{last_start}")
                            pass
                        else:
                            print(Fore.RED+f"Reason for error:current signal start bit less than last signal end bit!curr_start:{curr_start},last_start:{last_start}")
                            flag=False
                        last_start=curr_start+signal.length
                        #if signal.byte_order=='big_endian':
                            #print(Fore.GREEN+f"name:{signal.name},start bit:{start_bit_can2normal(signal.start)},length:{signal.length},byte_order:{signal.byte_order}")
                        #elif signal.byte_order=='little_endian':
                            #print(Fore.GREEN+f"name:{signal.name},start bit:{signal.start},length:{signal.length},byte_order:{signal.byte_order}")
                        #该部分主要用于判断是否是c源码中规范的类型
                        if sigName_l=='int':
                            erro_sig[message.name].append(sigName_l)
                            #print(Fore.GREEN + f"    {sigName_l}")
                        elif sigName_l=='long':
                            erro_sig[message.name].append(sigName_l)
                        elif sigName_l=='short':
                            erro_sig[message.name].append(sigName_l)
                        elif sigName_l=='char':
                            erro_sig[message.name].append(sigName_l)
                        elif sigName_l=='string':
                            erro_sig[message.name].append(sigName_l)
                        elif sigName_l=='float':
                            erro_sig[message.name].append(sigName_l)
                        elif sigName_l=='double':
                            erro_sig[message.name].append(sigName_l)
                        elif sigName_l=='wchar_t':
                            erro_sig[message.name].append(sigName_l)
                        else:
                            #print(Fore.RED + f"    {sigName_l}")
                            pass
                    #判断该message的长度是否大于所有signal累加的长度
                    if last_start>message.length*8:
                        print(Fore.RED+f"Reason for error:signal end bit greater than message length!curr_start:{curr_start},last_start:{last_start}")
                for key in erro_sig:
                    if key:
                        for i in erro_sig[key]:
                            hex_n=hex(int(mID[key]))
                            print(Fore.RED+f"Reason for error:error illegal variable name(ID:{hex_n}({mID[key]}),MESSAGE:{key},SIGNAL:{i})")
                            flag=False
                    else:
                        flag=True
            except Exception as e:
                flag=False
                print(Fore.RED+"Reason for error:",end='')
                print(sys.exc_info())
                # print(f'error file:{e.__traceback__.tb_frame.f_globals["__file__"]}')
                # print(f"error line:{e.__traceback__.tb_lineno}")
                            
            if flag:
                print(Fore.GREEN+f"{path}:Check pass!")
            else:
                print(Fore.RED+f"{path}: Error!")
                flag=True
                dbc_check=False

if dbc_check==False:
        print(Fore.RED+f"============all files dbc check failed!============")
        exit()
print(Fore.GREEN+f"============all files dbc check pass!============")

print(Fore.YELLOW+f"============dbc convert to xlsx============")
for file in files:
    path = os.path.join(root, file)
    if ".dbc" in path:
        command = "canconvert " + path + " " + path.replace(".dbc", ".xlsx")
        print(command)
        process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
        output, error = process.communicate()
        if error:
            print(f"An error occurred: {error}")
        else:
            print(output.decode())
            
        # Load the database
        db = cantools.database.load_file(path)
        # Prepare data for DataFrame
        data = []
        for message in db.messages:
            for signal in message.signals:
                signal_dict = signal.__dict__.copy()  # Get all properties of the signal
                signal_dict["message"] = message.name
                data.append(signal_dict)
    
        # Create a DataFrame and save to .xlsx
        df = pd.DataFrame(data)
        df.to_excel(path.replace(".dbc", "_value.xlsx"), index=False)
                
print(Fore.YELLOW+f"============dbc convert to .h & .c============")
for file in files:
    path = os.path.join(root, file)
    if ".dbc" in path:
        # Load the database
        # db = cantools.database.load_file(path)
                    # Generate the c source code
        # c_source_code = db.as_c_source()
        # print(c_source_code)
        
        command = "python3 -m cantools generate_c_source " + path
        process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
        output, error = process.communicate()
        if error:
            print(f"An error occurred: {error}")
        else:
            print(output.decode())
                
print(Fore.YELLOW+f"============auto print .h & .c============")
for root, dirs, files in os.walk(current_path):
		print(files)
for file in files:
    path = os.path.join(root, file)
    if ".h" in path:
        file_name = file
        if file_name[-2:]!='.h':
            print(Fore.RED+f"The file format is wrong, you must enter the .h file", file_name)
            sys.exit()
        if os.path.exists(file_name):
            print(f"File name:{sys.argv[1]}")
        else:
            print(f"The file does not exist.")
            sys.exit()
        print(f"---Automatic print file generation begins---")
        try:
            f1 = open(file_name, mode='r', encoding='utf-8')
            content = f1.read()
            f2 = open('print_'+file_name[:-2]+'.c','w')
            f3 = open('print_'+file_name[:-2]+'.h','w')
            f3.write('#pragma once\r\n#ifdef __cplusplus\r\nextern "C" {\r\n#endif\r\n\r\n#include <stdio.h>\r\n#include "'+file_name+'"\r\n\r\n')
            f2.write('#include "'+'print_'+file_name[:-2]+'.h'+'"\r\n\r\n')
            #print(type(content))
            struct_pos=0
            while content.find("struct ")!=-1:
                struct_pos=content.find("struct ")
                next_content=content[struct_pos:]
                kuohao_l_pos=next_content.find("{")
                if(kuohao_l_pos==-1):
                    break
                kuohao_r_pos=next_content.find("}");
                body_content=next_content[kuohao_l_pos:kuohao_r_pos]
                struct_name=next_content[7:kuohao_l_pos-1]
                print_str="void print_"+struct_name+"(const struct "+struct_name+" *dst_p){\r\n    if (!dst_p) return;\r\n"
                f2.write(print_str)
                f3.write("void print_"+struct_name+"(const struct "+struct_name+" *dst_p);\r\n")
                while(body_content.find("uint")!=-1 or body_content.find("int")!=-1):
                    if body_content.find("uint")!=-1:
                        uint_pos=body_content.find("uint")
                        if '0'<body_content[uint_pos+4]<'9':
                            pass
                        else:
                            body_content=body_content[uint_pos+4:]
                    elif body_content.find("int")!=-1:
                        uint_pos=body_content.find("int")
                        if '0'<body_content[uint_pos+3]<'9':
                            pass
                        else:
                            body_content=body_content[uint_pos+3:]
                            continue
                    # if uint_pos==-1 and int_pos!=-1:
                    #     uint_pos=int_pos
                    # if(int_pos<uint_pos):
                    #     uint_pos=int_pos
                    maohao_pos=body_content.find(";")
                    if(maohao_pos==-1):
                        break
                    space_pos=body_content.find(" ",uint_pos,maohao_pos)
                    variable_name=body_content[space_pos+1:maohao_pos]
                    if "\n" in variable_name:
                        #print("find now")
                        body_content=body_content[maohao_pos+1:]
                        continue
                    print_str='    printf("'+struct_name+"_"+variable_name+' raw_value[%d],physical_value[%f],value_range[%d]\\r\\n", dst_p->'+variable_name+','+next_content[7:kuohao_l_pos-2]+variable_name+'_decode(dst_p->'+variable_name+'),'+next_content[7:kuohao_l_pos-2]+variable_name+'_is_in_range(dst_p->'+variable_name+'));\r\n'
                    f2.write(print_str)
                    body_content=body_content[maohao_pos+1:]  
                print_str="}\r\n"
                f2.write(print_str)
                content=next_content[kuohao_r_pos:]
        
            f3.write("#ifdef __cplusplus\r\n}\r\n#endif\r\n")
            f3.close()
            f2.close()
            f1.close()
            print("Successfully generated!")
        except:
            print("Failed to generate...")
            sys.exit()  